﻿using Exiled.API.Enums;
using Exiled.API.Features;
using Exiled.Events.EventArgs.Player;
using Exiled.Events.EventArgs.Server;
using Newtonsoft.Json;
using RgCore.Database;
using RgCore.Modules;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace AltAccountRestriction
{
    public class Root
    {
        public Response response { get; set; }
    }
    public class GamesItem
    {
        public int appid { get; set; }
        public int playtime_forever { get; set; }
    }
    public class Response
    {
        public int game_count { get; set; }
        public List<GamesItem> games { get; set; }
    }
    public class Config : IConfig
    {
        [Description("注册时间限制（小时，0为无限制）")]
        public int RegistrationHourLimit { get; set; } = 168;
        [Description("等级限制，要求大于等于该值，0为无限制")]
        public int SteamLevelLimit { get; set; } = 1;
        [Description("游玩时间限制（小时，0为无限制）")]
        public int PlaytimeLimit { get; set; } = 50;
        [Description("启用VAC限制")]
        public bool VacLimit { get; set; } = true;
        [Description("启用短信验证白名单")]
        public bool EnableSmsVerification { get; set; } = true;
        [Description("Steam API Key")]
        public string ApiKey { get; set; } = "59D50A82829DED9FB2B110EC4FEB042D";
    }
    public class AltAccountRestriction : RgPlugin
    {
        public override string Name => "反小号插件";

        public override string Id => "alt_account_restriction";

        public override object Config { get; set; } = new Config();

        public override PluginPriority Priority => PluginPriority.Lowest;

        public const string GameStats = "https://api.steampowered.com/IPlayerService/GetOwnedGames/v1?key=%KEY%&steamid=%STEAMID%";
        public const string Summaries = "https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2?key=%KEY%&steamids=%STEAMID%";
        public const string SteamLevel = "https://api.steampowered.com/IPlayerService/GetSteamLevel/v1?key=%KEY%&steamid=%STEAMID%";
        public const string SteamBans = "https://api.steampowered.com/ISteamUser/GetPlayerBans/v1?key=%KEY%&steamids=%STEAMID%";

        public Dictionary<string, bool> PermissionCache = new Dictionary<string, bool>();
        public List<string> LastRoundWhitelist = new List<string>();
        public Dictionary<string, int> Processing = new Dictionary<string, int>();
        public override void OnDisabled()
        {
            base.OnDisabled();
            Exiled.Events.Handlers.Player.PreAuthenticating -= Preauth;
            Exiled.Events.Handlers.Server.RoundEnded -= OnRoundEnded;
            RgCore.RgCore.RoundStarted -= RgCore_RoundStarted;
        }

        public override void OnEnabled()
        {
            base.OnEnabled();
            Exiled.Events.Handlers.Player.PreAuthenticating += Preauth;
            Exiled.Events.Handlers.Server.RoundEnded += OnRoundEnded;
            RgCore.RgCore.RoundStarted += RgCore_RoundStarted;
        }

        private void RgCore_RoundStarted()
        {
            LastRoundWhitelist.Clear();
        }

        public void OnRoundEnded(RoundEndedEventArgs ev)
        {
            foreach (Player p in Player.List)
            {
                LastRoundWhitelist.Add(p.UserId);
            }
        }
        public void Preauth(PreAuthenticatingEventArgs ev)
        {
            if (ev.Flags.HasFlag(CentralAuthPreauthFlags.NorthwoodStaff))
                return;
            if (!ev.IsAllowed)
                return;
            if (LastRoundWhitelist.Contains(ev.UserId))
            {
                LastRoundWhitelist.Remove(ev.UserId);
                return;
            }
            if (!PermissionCache.ContainsKey(ev.UserId))
            {
                if (Processing.ContainsKey(ev.UserId))
                {
                    if (Processing[ev.UserId] < 3)
                    {
                        Processing[ev.UserId]++;
                        Log.Info(ev.UserId + " 等待" + Processing[ev.UserId] + "次");
                        ev.Delay(3, false);
                        return;
                    }
                    Log.Info(ev.UserId + " 认证超时");
                    Processing.Remove(ev.UserId);
                    ev.Reject("认证超时，请尝试再次加入", false);
                    return;
                }
                Check(ev.UserId);
                ev.Delay(2, false);
                return;
            }
            if (!PermissionCache[ev.UserId])
            {
                ev.Reject("[白名单] 您的账户尚不满足进服条件\n请访问 https://binding.railgungame.cn/ 或点击本服务器右边信息页进行白名单绑定\n", true);
            }
            PermissionCache.Remove(ev.UserId);
        }
        private async Task<string> GetHttpResponse(string uri)
        {
            string result = "";
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
            request.Method = "GET";
            request.ContentType = "text/html;charset=UTF-8";
            HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();
            Stream stream = response.GetResponseStream();
            using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
            {
                result = await reader.ReadToEndAsync();
            }
            stream.Close();
            stream.Dispose();
            response.Close();
            response.Dispose();
            return result;
        }
        private async Task<bool> CheckSteamLevel(string steamid)
        {
            Config conf = (Config)Config;
            string uri = SteamLevel.Replace("%KEY%", conf.ApiKey).Replace("%STEAMID%", steamid);
            string result = await GetHttpResponse(uri);
            if (!result.Contains("player_level"))
            {
                Log.Info($"{steamid} 不满足Steam等级条件（私密档案）");
                return false;
            }
            while (!result.StartsWith("player_level"))
                result = result.Remove(0, 1);
            int index = result.IndexOf('}');
            int level = int.Parse(result.Substring(14, index - 14));
            if (conf.SteamLevelLimit > level)
            {
                Log.Info($"{steamid} 不满足Steam等级条件");
                return false;
            }
            Log.Info("已通过Steam等级验证");
            return true;
        }
        private async Task<bool> CheckRegistration(string steamid)
        {
            Config conf = (Config)Config;
            string uri = Summaries.Replace("%KEY%", conf.ApiKey).Replace("%STEAMID%", steamid);
            string result = await GetHttpResponse(uri);
            if (!result.Contains("timecreated"))
            {
                Log.Info($"{steamid} 不满足注册时间条件（私密档案）");
                return false;
            }
            while (!result.StartsWith("timecreated"))
                result = result.Remove(0, 1);
            int index = result.IndexOf(',');
            long timestamp = long.Parse(result.Substring(13, index - 13));
            DateTime dt = new DateTime(new DateTime(1970, 1, 1, 8, 0, 0).Ticks + timestamp * 10000000);
            if ((DateTime.Now - dt).TotalHours < conf.RegistrationHourLimit)
            {
                Log.Info($"{steamid} 不满足注册时间条件");
                return false;
            }
            Log.Info("已通过注册时间验证");
            return true;
        }
        private async Task<bool> CheckPlaytime(string steamid)
        {
            Config conf = (Config)Config;
            string uri = GameStats.Replace("%KEY%", conf.ApiKey).Replace("%STEAMID%", steamid);
            string result = await GetHttpResponse(uri);
            Response r = JsonConvert.DeserializeObject<Root>(result).response;
            if (r.games == null)
            {
                Log.Info($"{steamid} 不满足游戏时间条件（私密档案）");
                return false;
            }
            var info = r.games.FirstOrDefault(x => x.appid == 700330);
            if (info == null || info.playtime_forever < (conf.PlaytimeLimit * 60))
            {
                Log.Info($"{steamid} 不满足游戏时间条件");
                return false;
            }
            Log.Info("已通过游玩时间验证");
            return true;
        }
        private async Task<bool> CheckVAC(string steamid)
        {
            Config conf = (Config)Config;
            string uri = SteamBans.Replace("%KEY%", conf.ApiKey).Replace("%STEAMID%", steamid);
            string result = await GetHttpResponse(uri);
            while (!result.StartsWith("VACBanned"))
                result = result.Remove(0, 1);
            int index = result.IndexOf(',');
            bool banned = bool.Parse(result.Substring(11, index - 11));
            if (banned)
            {
                Log.Info($"{steamid} 账户记录VAC封禁");
                return false;
            }
            Log.Info("已通过VAC验证");
            return true;
        }
        
        private async void Check(string userid)
        {
            Processing.Add(userid, 0);
            Config conf = (Config)Config;
            string steamid = userid.Replace("@steam", "");
            var filter = new Dictionary<string, object>() { { "steam_id", userid } };
            if (conf.EnableSmsVerification && !DatabaseController.DatabaseDisabled)
            {
                QueryCommandRequest cmd = new QueryCommandRequest("cdcp_phone_binding", filter, false, "steam_id");
                if ((await cmd.GetResponseResult()).Any())
                {
                    Log.Info("验证通过（手机号码验证）");
                    PermissionCache.Add(userid, true);
                    Processing.Remove(userid);
                    return;
                }
            }
            List<Task<bool>> tasks = new List<Task<bool>>();
            if (conf.SteamLevelLimit > 0)
                tasks.Add(CheckSteamLevel(steamid));
            if (DateTime.Now.Hour >= 2 && DateTime.Now.Hour < 9)
            {
                if (conf.RegistrationHourLimit > 0)
                    tasks.Add(CheckRegistration(steamid));
                if (conf.PlaytimeLimit > 0)
                    tasks.Add(CheckPlaytime(steamid));
                if (conf.VacLimit)
                    tasks.Add(CheckVAC(steamid));
            }
            var result = await Task.WhenAll(tasks);
            if (result.Any(x => !x))
            {
                PermissionCache.Add(userid, false);
                Processing.Remove(userid);
                return;
            }
            Log.Info("验证通过（小号检测）");
            PermissionCache.Add(userid, true);
            Processing.Remove(userid);
            return;
        }
    }
}
